{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "h2q27gKz1H20"
      },
      "source": [
        "##### Copyright 2024 The AI Edge Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "TUfAcER1oUS6"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Gb7qyhNL1yWt"
      },
      "source": [
        "# Object Detection with TensorFlow Lite Model Maker"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Fw5Y7snSuG51"
      },
      "source": [
        "\u003ctable class=\"tfo-notebook-buttons\" align=\"left\"\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://www.tensorflow.org/lite/models/modify/model_maker/object_detection\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" /\u003eView on TensorFlow.org\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/models/modify/model_maker/object_detection.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" /\u003eRun in Google Colab\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/models/modify/model_maker/object_detection.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" /\u003eView source on GitHub\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca href=\"https://storage.googleapis.com/tensorflow_docs/tensorflow/tensorflow/lite/g3doc/models/modify/model_maker/object_detection.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/download_logo_32px.png\" /\u003eDownload notebook\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "\u003c/table\u003e"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sr3q-gvm3cI8"
      },
      "source": [
        "In this colab notebook, you'll learn how to use the [TensorFlow Lite Model Maker](https://www.tensorflow.org/lite/models/modify/model_maker) library to train a custom object detection model capable of detecting salads within images on a mobile device.\n",
        "\n",
        "The Model Maker library uses *transfer learning* to simplify the process of training a TensorFlow Lite model using a custom dataset. Retraining a TensorFlow Lite model with your own custom dataset reduces the amount of training data required and will shorten the training time.\n",
        "\n",
        "You'll use the publicly available *Salads* dataset, which was created from the [Open Images Dataset V4](https://storage.googleapis.com/openimages/web/index.html).\n",
        "\n",
        "Each image in the dataset \u0008contains objects labeled as one of the following classes:\n",
        "* Baked Good\n",
        "* Cheese\n",
        "* Salad\n",
        "* Seafood\n",
        "* Tomato\n",
        "\n",
        "The dataset contains the bounding-boxes specifying where each object locates, together with the object's label.\n",
        "\n",
        "Here is an example image from the dataset:\n",
        "\n",
        "\u003cbr/\u003e\n",
        "\n",
        "\u003cimg src=\"https://cloud.google.com/vision/automl/object-detection/docs/images/quickstart-preparing_a_dataset.png\" width=\"400\" hspace=\"0\"\u003e\n",
        "\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bcLF2PKkSbV3"
      },
      "source": [
        "## Prerequisites\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2vvAObmTqglq"
      },
      "source": [
        "### Install the required packages\n",
        "Start by installing the required packages, including the Model Maker package from the [GitHub repo](https://github.com/tensorflow/examples/tree/master/tensorflow_examples/lite/model_maker) and the pycocotools library you'll use for evaluation."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "qhl8lqVamEty"
      },
      "outputs": [],
      "source": [
        "!sudo apt -y install libportaudio2\n",
        "!pip install -q --use-deprecated=legacy-resolver tflite-model-maker\n",
        "!pip install -q pycocotools\n",
        "!pip install -q opencv-python-headless==4.1.2.30\n",
        "!pip uninstall -y tensorflow \u0026\u0026 pip install -q tensorflow==2.8.0"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "l6lRhVK9Q_0U"
      },
      "source": [
        "Import the required packages."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "XtxiUeZEiXpt"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import os\n",
        "\n",
        "from tflite_model_maker.config import QuantizationConfig\n",
        "from tflite_model_maker.config import ExportFormat\n",
        "from tflite_model_maker import model_spec\n",
        "from tflite_model_maker import object_detector\n",
        "\n",
        "import tensorflow as tf\n",
        "assert tf.__version__.startswith('2')\n",
        "\n",
        "tf.get_logger().setLevel('ERROR')\n",
        "from absl import logging\n",
        "logging.set_verbosity(logging.ERROR)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BRd13bfetO7B"
      },
      "source": [
        "### Prepare the dataset\n",
        "\n",
        "Here you'll use the same dataset as the AutoML [quickstart](https://cloud.google.com/vision/automl/object-detection/docs/edge-quickstart#preparing_a_dataset).\n",
        "\n",
        "The *Salads* dataset is available at:\n",
        " `gs://cloud-ml-data/img/openimage/csv/salads_ml_use.csv`.\n",
        "\n",
        "It contains 175 images for training, 25 images for validation, and 25 images for testing. The dataset has five classes: `Salad`, `Seafood`, `Tomato`, `Baked goods`, `Cheese`.\n",
        "\n",
        "\u003cbr/\u003e\n",
        "\n",
        "The dataset is provided in CSV format:\n",
        "```\n",
        "TRAINING,gs://cloud-ml-data/img/openimage/3/2520/3916261642_0a504acd60_o.jpg,Salad,0.0,0.0954,,,0.977,0.957,,\n",
        "VALIDATION,gs://cloud-ml-data/img/openimage/3/2520/3916261642_0a504acd60_o.jpg,Seafood,0.0154,0.1538,,,1.0,0.802,,\n",
        "TEST,gs://cloud-ml-data/img/openimage/3/2520/3916261642_0a504acd60_o.jpg,Tomato,0.0,0.655,,,0.231,0.839,,\n",
        "```\n",
        "\n",
        "* Each row corresponds to an object localized inside a larger image, with each object specifically designated as test, train, or validation data. You'll learn more about what that means in a later stage in this notebook.\n",
        "* The three lines included here indicate **three distinct objects located inside the same image** available at `gs://cloud-ml-data/img/openimage/3/2520/3916261642_0a504acd60_o.jpg`.\n",
        "* Each row has a different label: `Salad`, `Seafood`, `Tomato`, etc.\n",
        "* Bounding boxes are specified for each image using the top left and bottom right vertices.\n",
        "\n",
        "Here is a visualization of these three lines:\n",
        "\n",
        "\u003cbr\u003e\n",
        "\n",
        "\u003cimg src=\"https://cloud.google.com/vision/automl/object-detection/docs/images/quickstart-preparing_a_dataset.png\" width=\"400\" hspace=\"100\"\u003e\n",
        "\n",
        "If you want to know more about how to prepare your own CSV file and the minimum requirements for creating a valid dataset, see the [Preparing your training data](https://cloud.google.com/vision/automl/object-detection/docs/prepare) guide for more details.\n",
        "\n",
        "If you are new to Google Cloud, you may wonder what the `gs://` URL means. They are URLs of files stored on [Google Cloud Storage](https://cloud.google.com/storage) (GCS). If you make your files on GCS public or [authenticate your client](https://cloud.google.com/storage/docs/authentication#libauth), Model Maker can read those files similarly to your local files.\n",
        "\n",
        "However, you don't need to keep your images on Google Cloud to use Model Maker. You can use a local path in your CSV file and Model Maker will just work."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xushUyZXqP59"
      },
      "source": [
        "## Quickstart"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vn61LJ9QbOPi"
      },
      "source": [
        "There are six steps to training an object detection model:\n",
        "\n",
        "**Step 1. Choose an object detection model architecture.**\n",
        "\n",
        "This tutorial uses the EfficientDet-Lite0 model. EfficientDet-Lite[0-4] are a family of mobile/IoT-friendly object detection models derived from the [EfficientDet](https://arxiv.org/abs/1911.09070) architecture.\n",
        "\n",
        "Here is the performance of each EfficientDet-Lite models compared to each others.\n",
        "\n",
        "| Model architecture | Size(MB)* | Latency(ms)** | Average Precision*** |\n",
        "|--------------------|-----------|---------------|----------------------|\n",
        "| EfficientDet-Lite0 | 4.4       | 37            | 25.69%               |\n",
        "| EfficientDet-Lite1 | 5.8       | 49            | 30.55%               |\n",
        "| EfficientDet-Lite2 | 7.2       | 69            | 33.97%               |\n",
        "| EfficientDet-Lite3 | 11.4      | 116           | 37.70%               |\n",
        "| EfficientDet-Lite4 | 19.9      | 260           | 41.96%               |\n",
        "\n",
        "\u003ci\u003e * Size of the integer quantized models. \u003cbr/\u003e\n",
        "** Latency measured on Pixel 4 using 4 threads on CPU. \u003cbr/\u003e\n",
        "*** Average Precision is the mAP (mean Average Precision) on the COCO 2017 validation dataset.\n",
        "\u003c/i\u003e\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "CtdZ-JDwMimd"
      },
      "outputs": [],
      "source": [
        "spec = model_spec.get('efficientdet_lite0')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "s5U-A3tw6Y27"
      },
      "source": [
        "**Step 2. Load the dataset.**\n",
        "\n",
        "Model Maker will take input data in the CSV format. Use the `object_detector.DataLoader.from_csv` method to load the dataset and split them into the training, validation and test images.\n",
        "\n",
        "* Training images: These images are used to train the object detection model to recognize salad ingredients.\n",
        "* Validation images: These are images that the model didn't see during the training process. You'll use them to decide when you should stop the training, to avoid [overfitting](https://en.wikipedia.org/wiki/Overfitting).\n",
        "* Test images: These images are used to evaluate the final model performance.\n",
        "\n",
        "You can load the CSV file directly from Google Cloud Storage, but you don't need to keep your images on Google Cloud to use Model Maker. You can specify a local CSV file on your computer, and Model Maker will work just fine."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "HD5BvzWe6YKa"
      },
      "outputs": [],
      "source": [
        "train_data, validation_data, test_data = object_detector.DataLoader.from_csv('gs://cloud-ml-data/img/openimage/csv/salads_ml_use.csv')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2uZkLR6N6gDR"
      },
      "source": [
        "**Step 3. Train the TensorFlow model with the training data.**\n",
        "\n",
        "* The EfficientDet-Lite0 model uses `epochs = 50` by default, which means it will go through the training dataset 50 times. You can look at the validation accuracy during training and stop early to avoid overfitting.\n",
        "* Set `batch_size = 8` here so you will see that it takes 21 steps to go through the 175 images in the training dataset.\n",
        "* Set `train_whole_model=True` to fine-tune the whole model instead of just training the head layer to improve accuracy. The trade-off is that it may take longer to train the model."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "kwlYdTcg63xy"
      },
      "outputs": [],
      "source": [
        "model = object_detector.create(train_data, model_spec=spec, batch_size=8, train_whole_model=True, validation_data=validation_data)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-BzCHLWJ6h7q"
      },
      "source": [
        "**Step 4. Evaluate the model with the test data.**\n",
        "\n",
        "After training the object detection model using the images in the training dataset, use the remaining 25 images in the test dataset to evaluate how the model performs against new data it has never seen before.\n",
        "\n",
        "As the default batch size is 64, it will take 1 step to go through the 25 images in the test dataset.\n",
        "\n",
        "The evaluation metrics are same as [COCO](https://cocodataset.org/#detection-eval)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8xmnl6Yy7ARn"
      },
      "outputs": [],
      "source": [
        "model.evaluate(test_data)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CgCDMe0e6jlT"
      },
      "source": [
        "**Step 5.  Export as a TensorFlow Lite model.**\n",
        "\n",
        "Export the trained object detection model to the TensorFlow Lite format by specifying which folder you want to export the quantized model to. The default post-training quantization technique is full integer quantization."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Hm_UULdW7A9T"
      },
      "outputs": [],
      "source": [
        "model.export(export_dir='.')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ZQpahAIBqBPp"
      },
      "source": [
        "**Step 6.  Evaluate the TensorFlow Lite model.**\n",
        "\n",
        "Several factors can affect the model accuracy when exporting to TFLite:\n",
        "* [Quantization](https://www.tensorflow.org/lite/performance/model_optimization) helps shrinking the model size by 4 times at the expense of some accuracy drop.\n",
        "* The original TensorFlow model uses per-class [non-max supression (NMS)](https://www.coursera.org/lecture/convolutional-neural-networks/non-max-suppression-dvrjH) for post-processing, while the TFLite model uses global NMS that's much faster but less accurate.\n",
        "Keras outputs maximum 100 detections while tflite outputs maximum 25 detections.\n",
        "\n",
        "Therefore you'll have to evaluate the exported TFLite model and compare its accuracy with the original TensorFlow model."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "RS3Ell_lqH4e"
      },
      "outputs": [],
      "source": [
        "model.evaluate_tflite('model.tflite', test_data)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rVxaf3x_7OfB"
      },
      "source": [
        "You can download the TensorFlow Lite model file using the left sidebar of Colab. Right-click on the `model.tflite` file and choose `Download` to download it to your local computer.\n",
        "\n",
        "This model can be integrated into an Android or an iOS app using the [ObjectDetector API](https://www.tensorflow.org/lite/inference_with_metadata/task_library/object_detector) of the [TensorFlow Lite Task Library](https://www.tensorflow.org/lite/inference_with_metadata/task_library/overview).\n",
        "\n",
        "See the [TFLite Object Detection sample app](https://github.com/tensorflow/examples/tree/master/lite/examples/object_detection/android) for more details on how the model is used in an working app.\n",
        "\n",
        "*Note: Android Studio Model Binding does not support object detection yet so please use the TensorFlow Lite Task Library.*"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "me6_RwPZqNhX"
      },
      "source": [
        "## (Optional) Test the TFLite model on your image\n",
        "\n",
        "You can test the trained TFLite model using images from the internet.\n",
        "* Replace the `INPUT_IMAGE_URL` below with your desired input image.\n",
        "* Adjust the `DETECTION_THRESHOLD` to change the sensitivity of the model. A lower threshold means the model will pickup more objects but there will also be more false detection. Meanwhile, a higher threshold means the model will only pickup objects that it has confidently detected.\n",
        "\n",
        "Although it requires some of boilerplate code to run the model in Python at this moment, integrating the model into a mobile app only requires a few lines of code."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "XqS0rFCrqM1o"
      },
      "outputs": [],
      "source": [
        "#@title Load the trained TFLite model and define some visualization functions\n",
        "\n",
        "import cv2\n",
        "\n",
        "from PIL import Image\n",
        "\n",
        "model_path = 'model.tflite'\n",
        "\n",
        "# Load the labels into a list\n",
        "classes = ['???'] * model.model_spec.config.num_classes\n",
        "label_map = model.model_spec.config.label_map\n",
        "for label_id, label_name in label_map.as_dict().items():\n",
        "  classes[label_id-1] = label_name\n",
        "\n",
        "# Define a list of colors for visualization\n",
        "COLORS = np.random.randint(0, 255, size=(len(classes), 3), dtype=np.uint8)\n",
        "\n",
        "def preprocess_image(image_path, input_size):\n",
        "  \"\"\"Preprocess the input image to feed to the TFLite model\"\"\"\n",
        "  img = tf.io.read_file(image_path)\n",
        "  img = tf.io.decode_image(img, channels=3)\n",
        "  img = tf.image.convert_image_dtype(img, tf.uint8)\n",
        "  original_image = img\n",
        "  resized_img = tf.image.resize(img, input_size)\n",
        "  resized_img = resized_img[tf.newaxis, :]\n",
        "  resized_img = tf.cast(resized_img, dtype=tf.uint8)\n",
        "  return resized_img, original_image\n",
        "\n",
        "\n",
        "def detect_objects(interpreter, image, threshold):\n",
        "  \"\"\"Returns a list of detection results, each a dictionary of object info.\"\"\"\n",
        "\n",
        "  signature_fn = interpreter.get_signature_runner()\n",
        "\n",
        "  # Feed the input image to the model\n",
        "  output = signature_fn(images=image)\n",
        "\n",
        "  # Get all outputs from the model\n",
        "  count = int(np.squeeze(output['output_0']))\n",
        "  scores = np.squeeze(output['output_1'])\n",
        "  classes = np.squeeze(output['output_2'])\n",
        "  boxes = np.squeeze(output['output_3'])\n",
        "\n",
        "  results = []\n",
        "  for i in range(count):\n",
        "    if scores[i] \u003e= threshold:\n",
        "      result = {\n",
        "        'bounding_box': boxes[i],\n",
        "        'class_id': classes[i],\n",
        "        'score': scores[i]\n",
        "      }\n",
        "      results.append(result)\n",
        "  return results\n",
        "\n",
        "\n",
        "def run_odt_and_draw_results(image_path, interpreter, threshold=0.5):\n",
        "  \"\"\"Run object detection on the input image and draw the detection results\"\"\"\n",
        "  # Load the input shape required by the model\n",
        "  _, input_height, input_width, _ = interpreter.get_input_details()[0]['shape']\n",
        "\n",
        "  # Load the input image and preprocess it\n",
        "  preprocessed_image, original_image = preprocess_image(\n",
        "      image_path,\n",
        "      (input_height, input_width)\n",
        "    )\n",
        "\n",
        "  # Run object detection on the input image\n",
        "  results = detect_objects(interpreter, preprocessed_image, threshold=threshold)\n",
        "\n",
        "  # Plot the detection results on the input image\n",
        "  original_image_np = original_image.numpy().astype(np.uint8)\n",
        "  for obj in results:\n",
        "    # Convert the object bounding box from relative coordinates to absolute\n",
        "    # coordinates based on the original image resolution\n",
        "    ymin, xmin, ymax, xmax = obj['bounding_box']\n",
        "    xmin = int(xmin * original_image_np.shape[1])\n",
        "    xmax = int(xmax * original_image_np.shape[1])\n",
        "    ymin = int(ymin * original_image_np.shape[0])\n",
        "    ymax = int(ymax * original_image_np.shape[0])\n",
        "\n",
        "    # Find the class index of the current object\n",
        "    class_id = int(obj['class_id'])\n",
        "\n",
        "    # Draw the bounding box and label on the image\n",
        "    color = [int(c) for c in COLORS[class_id]]\n",
        "    cv2.rectangle(original_image_np, (xmin, ymin), (xmax, ymax), color, 2)\n",
        "    # Make adjustments to make the label visible for all objects\n",
        "    y = ymin - 15 if ymin - 15 \u003e 15 else ymin + 15\n",
        "    label = \"{}: {:.0f}%\".format(classes[class_id], obj['score'] * 100)\n",
        "    cv2.putText(original_image_np, label, (xmin, y),\n",
        "        cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)\n",
        "\n",
        "  # Return the final image\n",
        "  original_uint8 = original_image_np.astype(np.uint8)\n",
        "  return original_uint8"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "GkXtipXKqXp4"
      },
      "outputs": [],
      "source": [
        "#@title Run object detection and show the detection results\n",
        "\n",
        "INPUT_IMAGE_URL = \"https://storage.googleapis.com/cloud-ml-data/img/openimage/3/2520/3916261642_0a504acd60_o.jpg\" #@param {type:\"string\"}\n",
        "DETECTION_THRESHOLD = 0.3 #@param {type:\"number\"}\n",
        "\n",
        "TEMP_FILE = '/tmp/image.png'\n",
        "\n",
        "!wget -q -O $TEMP_FILE $INPUT_IMAGE_URL\n",
        "im = Image.open(TEMP_FILE)\n",
        "im.thumbnail((512, 512), Image.ANTIALIAS)\n",
        "im.save(TEMP_FILE, 'PNG')\n",
        "\n",
        "# Load the TFLite model\n",
        "interpreter = tf.lite.Interpreter(model_path=model_path)\n",
        "interpreter.allocate_tensors()\n",
        "\n",
        "# Run inference and draw detection result on the local copy of the original file\n",
        "detection_result_image = run_odt_and_draw_results(\n",
        "    TEMP_FILE,\n",
        "    interpreter,\n",
        "    threshold=DETECTION_THRESHOLD\n",
        ")\n",
        "\n",
        "# Show the detection result\n",
        "Image.fromarray(detection_result_image)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "oxgWQyYOqZha"
      },
      "source": [
        "## (Optional) Compile For the Edge TPU\n",
        "\n",
        "Now that you have a quantized EfficientDet Lite model, it is possible to compile and deploy to a [Coral EdgeTPU](https://coral.ai/).\n",
        "\n",
        "**Step 1. Install the EdgeTPU Compiler**"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Oy3QIn_YqaRP"
      },
      "outputs": [],
      "source": [
        "! curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -\n",
        "\n",
        "! echo \"deb https://packages.cloud.google.com/apt coral-edgetpu-stable main\" | sudo tee /etc/apt/sources.list.d/coral-edgetpu.list\n",
        "\n",
        "! sudo apt-get update\n",
        "\n",
        "! sudo apt-get install edgetpu-compiler"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qRWewhqFqeL_"
      },
      "source": [
        "**Step 2. Select number of Edge TPUs, Compile**\n",
        "\n",
        "The EdgeTPU has 8MB of SRAM for caching model parameters ([more info](https://coral.ai/docs/edgetpu/compiler/#parameter-data-caching)). This means that for models that are larger than 8MB, inference time will be increased in order to transfer over model parameters. One way to avoid this is [Model Pipelining](https://coral.ai/docs/edgetpu/pipeline/) - splitting the model into segments that can have a dedicated EdgeTPU. This can significantly improve latency.\n",
        "\n",
        "The below table can be used as a reference for the number of Edge TPUs to use - the larger models will not compile for a single TPU as the intermediate tensors can't fit in on-chip memory.\n",
        "\n",
        "| Model architecture | Minimum TPUs | Recommended TPUs\n",
        "|--------------------|-------|-------|\n",
        "| EfficientDet-Lite0 | 1     | 1     |\n",
        "| EfficientDet-Lite1 | 1     | 1     |\n",
        "| EfficientDet-Lite2 | 1     | 2     |\n",
        "| EfficientDet-Lite3 | 2     | 2     |\n",
        "| EfficientDet-Lite4 | 2     | 3     |"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "LZdonJGCqieU"
      },
      "outputs": [],
      "source": [
        "NUMBER_OF_TPUS =  1#@param {type:\"number\"}\n",
        "\n",
        "!edgetpu_compiler model.tflite --num_segments=$NUMBER_OF_TPUS"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-g6_KQXnqlTC"
      },
      "source": [
        "**Step 3. Download, Run Model**\n",
        "\n",
        "With the model(s) compiled, they can now be run on EdgeTPU(s) for object detection. First, download the compiled TensorFlow Lite model file using the left sidebar of Colab. Right-click on the `model_edgetpu.tflite` file and choose `Download` to download it to your local computer."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VkQFz_qzqrrA"
      },
      "source": [
        "Now you can run the model in your preferred manner. Examples of detection include:\n",
        "* [pycoral detection](https://github.com/google-coral/pycoral/blob/master/examples/detect_image.py)\n",
        "* [Basic TFLite detection](https://github.com/google-coral/tflite/tree/master/python/examples/detection)\n",
        "* [Example Video Detection](https://github.com/google-coral/examples-camera)\n",
        "* [libcoral C++ API](https://github.com/google-coral/libcoral)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "EoWiA_zX8rxE"
      },
      "source": [
        "## Advanced Usage\n",
        "\n",
        "This section covers advanced usage topics like adjusting the model and the training hyperparameters."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "p79NHCx0xFqb"
      },
      "source": [
        "### Load the dataset\n",
        "\n",
        "#### Load your own data\n",
        "\n",
        "You can upload your own dataset to work through this tutorial. Upload your dataset by using the left sidebar in Colab.\n",
        "\n",
        "\u003cimg src=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/screenshots/model_maker_object_detection.png\" alt=\"Upload File\" width=\"1000\" hspace=\"0\"\u003e\n",
        "\n",
        "If you prefer not to upload your dataset to the cloud, you can also locally run the library by following the [guide](https://github.com/tensorflow/examples/tree/master/tensorflow_examples/lite/model_maker).\n",
        "\n",
        "#### Load your data with a different data format\n",
        "\n",
        "The Model Maker library also supports the `object_detector.DataLoader.from_pascal_voc` method to load data with [PASCAL VOC](https://towardsdatascience.com/coco-data-format-for-object-detection-a4c5eaf518c5#:~:text=Pascal%20VOC%20is%20an%20XML,for%20training%2C%20testing%20and%20validation) format. [makesense.ai](https://www.makesense.ai/) and [LabelImg](https://github.com/tzutalin/labelImg) are the tools that can annotate the image and save annotations as XML files in PASCAL VOC data format:\n",
        "```python\n",
        "object_detector.DataLoader.from_pascal_voc(image_dir, annotations_dir, label_map={1: \"person\", 2: \"notperson\"})\n",
        "```\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "E8VxPiOLy4Gv"
      },
      "source": [
        "### Customize the EfficientDet model hyperparameters\n",
        "\n",
        "The model and training pipeline parameters you can adjust are:\n",
        "\n",
        "* `model_dir`: The location to save the model checkpoint files. If not set, a temporary directory will be used.\n",
        "* `steps_per_execution`: Number of steps per training execution.\n",
        "* `moving_average_decay`: Float. The decay to use for maintaining moving averages of the trained parameters.\n",
        "* `var_freeze_expr`: The regular expression to map the prefix name of variables to be frozen which means remaining the same during training. More specific, use `re.match(var_freeze_expr, variable_name)` in the codebase to map the variables to be frozen.\n",
        "* `tflite_max_detections`: integer, 25 by default. The max number of output detections in the TFLite model.\n",
        "* `strategy`:  A string specifying which distribution strategy to use. Accepted values are 'tpu', 'gpus', None. tpu' means to use TPUStrategy. 'gpus' mean to use MirroredStrategy for multi-gpus. If None, use TF default with OneDeviceStrategy.\n",
        "* `tpu`:  The Cloud TPU to use for training. This should be either the name used when creating the Cloud TPU, or a grpc://ip.address.of.tpu:8470 url.\n",
        "* `use_xla`: Use XLA even if strategy is not tpu. If strategy is tpu, always use XLA, and this flag has no effect.\n",
        "* `profile`: Enable profile mode.\n",
        "* `debug`: Enable debug mode.\n",
        "\n",
        "Other parameters that can be adjusted is shown in [hparams_config.py](https://github.com/google/automl/blob/df451765d467c5ed78bbdfd632810bc1014b123e/efficientdet/hparams_config.py#L170).\n",
        "\n",
        "\n",
        "For instance, you can set the `var_freeze_expr='efficientnet'` which freezes the variables with name prefix `efficientnet` (default is `'(efficientnet|fpn_cells|resample_p6)'`). This allows the model to freeze untrainable variables and keep their value the same through training.\n",
        "\n",
        "```python\n",
        "spec = model_spec.get('efficientdet_lite0')\n",
        "spec.config.var_freeze_expr = 'efficientnet'\n",
        "```"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4J2qre1fwXsi"
      },
      "source": [
        "### Change the Model Architecture\n",
        "\n",
        "You can change the model architecture by changing the `model_spec`. For instance, change the `model_spec` to the EfficientDet-Lite4 model.\n",
        "\n",
        "```python\n",
        "spec = model_spec.get('efficientdet_lite4')\n",
        "```"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LvQuy7RSDir3"
      },
      "source": [
        "### Tune the training hyperparameters\n",
        "\n",
        "The `create` function is the driver function that the Model Maker library uses to create models. The `model_spec` parameter defines the model specification. The `object_detector.EfficientDetSpec` class is currently supported. The `create` function comprises of the following steps:\n",
        "\n",
        "1. Creates the model for the object detection according to `model_spec`.\n",
        "2. Trains the model.  The default epochs and the default batch size are set by the `epochs` and `batch_size` variables in the `model_spec` object.\n",
        "You can also tune the training hyperparameters like `epochs` and `batch_size` that affect the model accuracy. For instance,\n",
        "\n",
        "*   `epochs`: Integer, 50 by default. More epochs could achieve better accuracy, but may lead to overfitting.\n",
        "*   `batch_size`: Integer, 64 by default. The number of samples to use in one training step.\n",
        "*   `train_whole_model`: Boolean, False by default. If true, train the whole model. Otherwise, only train the layers that do not match `var_freeze_expr`.\n",
        "\n",
        "For example, you can train with less epochs and only the head layer. You can increase the number of epochs for better results.\n",
        "\n",
        "```python\n",
        "model = object_detector.create(train_data, model_spec=spec, epochs=10, validation_data=validation_data)\n",
        "```"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3vPyZInPxJBT"
      },
      "source": [
        "### Export to different formats"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0xqNIcBM-4YR"
      },
      "source": [
        "The export formats can be one or a list of the following:\n",
        "\n",
        "*   `ExportFormat.TFLITE`\n",
        "*   `ExportFormat.LABEL`\n",
        "*   `ExportFormat.SAVED_MODEL`\n",
        "\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "enhsZhW3ApcX"
      },
      "source": [
        "By default, it exports only the TensorFlow Lite model file containing the model [metadata](https://www.tensorflow.org/lite/models/convert/metadata) so that you can later use in an on-device ML application. The label file is embedded in metadata.\n",
        "\n",
        "In many on-device ML application, the model size is an important factor. Therefore, it is recommended that you quantize the model to make it smaller and potentially run faster. As for EfficientDet-Lite models, full integer quantization  is used to quantize the model by default. Please refer to [Post-training quantization](https://www.tensorflow.org/lite/performance/post_training_quantization) for more detail.\n",
        "\n",
        "```python\n",
        "model.export(export_dir='.')\n",
        "```"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RLGZs6InAnP5"
      },
      "source": [
        "You can also choose to export other files related to the model for better examination. For instance, exporting both the saved model and the label file as follows:\n",
        "```python\n",
        "model.export(export_dir='.', export_format=[ExportFormat.SAVED_MODEL, ExportFormat.LABEL])\n",
        "```"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "W5q_McchQ2C4"
      },
      "source": [
        "### Customize Post-training quantization on the TensorFlow Lite model\n",
        "\n",
        "[Post-training quantization](https://www.tensorflow.org/lite/performance/post_training_quantization) is a conversion technique that can reduce model size and inference latency, while also improving CPU and hardware accelerator inference speed, with a little degradation in model accuracy. Thus, it's widely used to optimize the model.\n",
        "\n",
        "Model Maker library applies a default post-training quantization technique when exporting the model. If you want to customize post-training quantization, Model Maker supports multiple post-training quantization options using [QuantizationConfig](https://www.tensorflow.org/lite/api_docs/python/tflite_model_maker/config/QuantizationConfig) as well. Let's take float16 quantization as an instance. First, define the quantization config.\n",
        "\n",
        "```python\n",
        "config = QuantizationConfig.for_float16()\n",
        "```\n",
        "\n",
        "\n",
        "Then we export the TensorFlow Lite model with such configuration.\n",
        "\n",
        "```python\n",
        "model.export(export_dir='.', tflite_filename='model_fp16.tflite', quantization_config=config)\n",
        "```"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HS4u77W5gnzQ"
      },
      "source": [
        "# Read more\n",
        "\n",
        "You can read our [object detection](https://www.tensorflow.org/lite/examples/object_detection/overview) example to learn technical details. For more information, please refer to:\n",
        "\n",
        "*   TensorFlow Lite Model Maker [guide](https://www.tensorflow.org/lite/models/modify/model_maker) and [API reference](https://www.tensorflow.org/lite/api_docs/python/tflite_model_maker).\n",
        "*   Task Library: [ObjectDetector](https://www.tensorflow.org/lite/inference_with_metadata/task_library/object_detector) for deployment.\n",
        "*   The end-to-end reference apps: [Android](https://github.com/tensorflow/examples/tree/master/lite/examples/object_detection/android), [iOS](https://github.com/tensorflow/examples/tree/master/lite/examples/object_detection/ios), and [Raspberry PI](https://github.com/tensorflow/examples/tree/master/lite/examples/object_detection/raspberry_pi).\n",
        "\n"
      ]
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "collapsed_sections": [],
      "name": "Model Maker Object Detection Tutorial",
      "provenance": [
        {
          "file_id": "1dbRXQCjtm-jBFC32DJ6YCVXnXBOG3M5t",
          "timestamp": 1613441434239
        },
        {
          "file_id": "https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/models/modify/model_maker/text_classification.ipynb",
          "timestamp": 1612303859066
        }
      ],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
